﻿using System;
using System.IO;
using System.Linq;
using System.Net;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;

namespace CrmRulo.v4
{
    class ReportManager : IReportManager
    {
        private readonly CrmService _crm;

        public ReportManager(string connectionString)
        {
            _crm = CreateCrmServiceFromConnnectionString(connectionString);
        }

        private static CrmService CreateCrmServiceFromConnnectionString(string connectionString)
        {
            var connectionParts = connectionString.Split(';').Select(x => x.Trim());
            var findUrl = connectionParts.FirstOrDefault(x => x.StartsWith("Url="));
            if (findUrl == null)
                throw new Exception("Url parameter not found in connection string.");
            var uri = new Uri(findUrl.Substring(4));

            var crmServiceEndpoint = String.Format("{0}://{1}:{2}/MSCRMServices/2007/crmservice.asmx", uri.Scheme, uri.Host, uri.Port);
            if (uri.Segments.Length != 2)
                throw new Exception("Url parameter incorrect in connection string. Format should be 'http://server:port/OrgName'");
            var organizationName = uri.Segments[1];
            var crm = new CrmService
            {
                UseDefaultCredentials = true,
                Url = crmServiceEndpoint,
                CrmAuthenticationTokenValue = new CrmAuthenticationToken { OrganizationName = organizationName }
            };

            var findUsername = connectionParts.FirstOrDefault(x => x.StartsWith("Username="));
            if (findUsername != null)
            {
                string username = null;
                username = findUsername.Substring(9);
                var findDomain = connectionParts.FirstOrDefault(x => x.StartsWith("Domain="));
                var domain = findDomain.Substring(7);
                var findPassword = connectionParts.FirstOrDefault(x => x.StartsWith("Password="));
                var password = findPassword.Substring(9);
                crm.UseDefaultCredentials = false;
                crm.Credentials = new NetworkCredential(username, password, domain);
            }

            return crm;
        }


        private report ConvertFromModel(Report r)
        {
            string reportText = File.ReadAllText(r.PathToReportFile);
            string reportFileName = Path.GetFileName(r.PathToReportFile);
            var report = new report
                             {
                                 name = r.Name,
                                 filename = reportFileName,
                                 bodytext = reportText,
                                 reporttypecode = new Picklist(1), // Reporting services
                                 languagecode = new CrmNumber(r.LanguageCode),
                                 ispersonal = new CrmBoolean(false),
                                 description = r.Description
                             };
            if (!String.IsNullOrEmpty(r.ParentReportName))
            {
                var parentReport = FindReport(r.ParentReportName);
                report.parentreportid = new Lookup("report", parentReport.Id.Value);
            }
            if (r.Id.HasValue)
                report.reportid = new Key(r.Id.Value);
            return report;
        }

        public Report FindReport(string reportName)
        {
            var q = new QueryByAttribute
            {
                EntityName = "report",
                ColumnSet = new ColumnSet(new[] { "reportid", "modifiedon" }),
                Attributes = new[] { "name" },
                Values = new[] { reportName }
            };
            var res = _crm.RetrieveMultiple(q);
            if (res.BusinessEntities.Count == 0)
                return null;
            if (res.BusinessEntities.Count > 1)
                throw new Exception("More than one report found with name: " + reportName);
            var crmReport = (report)res.BusinessEntities[0];
            var report = new Report
                             {
                                 Id = crmReport.reportid.Value,
                                 LastModified = crmReport.modifiedon.UserTime
                             };
            return report;
        }

        public Guid CreateReport(Report r)
        {
            var crmReport = ConvertFromModel(r);
            return _crm.Create(crmReport);
        }

        public void UpdateReport(Report r)
        {
            var crmReport = ConvertFromModel(r);
            _crm.Update(crmReport);
        }

        public void AddCategory(Guid reportId, int category)
        {
            var reportCategory = new reportcategory
            {
                reportid = new Lookup("report", reportId),
                categorycode = new Picklist(category)
            };
            _crm.Create(reportCategory);
        }

        /// <summary>
        /// Deletes all auxiliary reportcategory records for a report
        /// v4.0
        /// </summary>
        /// <param name="reportId">The report id</param>
        public void RemoveCategoryInformation(Guid reportId)
        {
            var q = new QueryByAttribute
            {
                EntityName = "reportcategory",
                Attributes = new[] { "reportid" },
                Values = new[] { reportId.ToString() }
            };

            var res = _crm.RetrieveMultiple(q);
            foreach (reportcategory rc in res.BusinessEntities)
                _crm.Delete("reportcategory", rc.reportcategoryid.Value);
        }

        /// <summary>
        /// Creates an auxiliary reportvisibility record for a report
        /// v4.0
        /// </summary>
        /// <param name="reportId">The report id</param>
        /// <param name="visibility">The visility value identificator (see ReportVisibilityCode enum)</param>
        public void AddVisibility(Guid reportId, int visibility)
        {
            var rv = new reportvisibility
            {
                reportid = new Lookup("report", reportId),
                visibilitycode = new Picklist(visibility)
            };
            _crm.Create(rv);
        }

        /// <summary>
        /// Deletes all auxiliary reportvisibility records for a report
        /// v4.0
        /// </summary>
        /// <param name="reportId">The report id</param>
        public void RemoveVisibilityInformation(Guid reportId)
        {
            var q = new QueryByAttribute
            {
                EntityName = "reportvisibility",
                Attributes = new[] { "reportid" },
                Values = new[] { reportId.ToString()
            }
            };
            var res = _crm.RetrieveMultiple(q);
            foreach (reportvisibility rv in res.BusinessEntities)
                _crm.Delete("reportvisibility", rv.reportvisibilityid.Value);
        }

        /// <summary>
        /// Creates an auxiliary reportentity record for a report
        /// v4.0
        /// </summary>
        /// <param name="reportId">The report id</param>
        /// <param name="entityLogicalName">The entity name to attach the report to</param>
        public void AddEntityCode(Guid reportId, string entityLogicalName)
        {
            var re = new reportentity
            {
                reportid = new Lookup("report", reportId),
                objecttypecode = new EntityNameReference(entityLogicalName)
            };
            _crm.Create(re);
        }

        /// <summary>
        /// Deletes all auxiliary reportentity records for a report
        /// v4.0
        /// </summary>
        /// <param name="reportId">The report id</param>
        public void RemoveEntityCodeInformation(Guid reportId)
        {
            var q = new QueryByAttribute
            {
                EntityName = "reportentity",
                Attributes = new[] { "reportid" },
                Values = new[] { reportId.ToString() }
            };

            var res = _crm.RetrieveMultiple(q);
            foreach (reportentity re in res.BusinessEntities)
                _crm.Delete("reportentity", re.reportentityid.Value);
        }



    }
}
